#include"dns_route.h"
#include<string.h>
#include"subscribe.h"

// 单例初始化
Route* Route::_instance = NULL;
pthread_once_t Route::_once = PTHREAD_ONCE_INIT;


// 构造函数
Route::Route(){
    // printf("Route init\n");

    // 初始化map的读写锁
    pthread_rwlock_init(&_map_lock, NULL);

    // 初始化当前的map指针
    _data_pointer = new route_map();
    _temp_pointer = new route_map();

    // 初始化时连接数据库
    this->connect_db();

    // 将数据库中的RouteDta表中的数据加载到_data_pointer中
    this->build_maps();

    // this->test_map();

    // 初始化版本信息
    _version = 0;

}



// 连接数据库的函数
void Route::connect_db(){
    // 加载Mysql的配置文件
    std::string db_host = config_file::instance()->GetString("mysql", "db_host", "127.0.0.1");
    short db_port = config_file::instance()->GetNumber("mysql", "db_port", 3306);
    std::string db_user = config_file::instance()->GetString("mysql", "db_user", "root");
    std::string db_pwd = config_file::instance()->GetString("mysql", "db_pwd", "123456");
    std::string db_name = config_file::instance()->GetString("mysql", "db_name", "lars_dns");

    // printf("mysql connect: db_host:%s, db_port:%d, db_user:%s, db_pwd:%s, db_name:%s\n", db_host.c_str(), db_port, db_user.c_str(), db_pwd.c_str(), db_name.c_str());
    // 初始化数据库
    mysql_init(&_db_conn);

    // 设置超时定期重连参数
    mysql_options(&_db_conn, MYSQL_OPT_CONNECT_TIMEOUT, (void *)"30");
    // 设置允许重连
    my_bool reconnect = 1;
    mysql_options(&_db_conn, MYSQL_OPT_RECONNECT, (void *)&reconnect);

    // 连接数据库
    if(!mysql_real_connect(&_db_conn, db_host.c_str(), db_user.c_str(), db_pwd.c_str(), db_name.c_str(), db_port, NULL, 0)){
        printf("%s\n", mysql_error(&_db_conn));
        exit(1);
    }

    printf("mysql connect success!\n");

}


// 构建map数据，将RouteData表中的数据加载到_data_pointer中
void Route::build_maps(){

    // // 查询RouteData数据库
    // memset(&_sql, 0, 1000);
    // snprintf(_sql, 1000, "SELECT * FROM RouteData;");
    // int ret = mysql_real_query(&_db_conn, _sql, strlen(_sql));
    // if(ret != 0){
    //     fprintf(stderr, "mysql_real_query error: %s\n", mysql_error(&_db_conn));
    //     exit(1);
    // }

    // // 获得一个结果集合
    // MYSQL_RES *result = mysql_store_result(&_db_conn);
    // // 得到行数
    // long line_num = mysql_num_rows(result);

    // MYSQL_ROW row;
    // // 遍历分析集合中的元素，加入到_data_pointer中
    // for(int i=0; i<line_num; i++){
    //     // 处理一行的数据
    //     row = mysql_fetch_row(result);

    //     int modId = atoi(row[1]);
    //     int cmdId = atoi(row[2]);
    //     unsigned int serverIp = atoi(row[3]);
    //     int serverPort = atoi(row[4]);

    //     // printf("modId:%d, cmdId:%d, serverIp:%u, serverPort:%d\n", modId, cmdId, serverIp, serverPort);
        
    //     // 组装map的key
    //     uint64_t key = ((uint64_t)modId<<32) + cmdId;
    //     // 组装map的value
    //     uint64_t value = ((uint64_t)serverIp<<32) + serverPort;
        
    //     // std::cout << "key:" << key <<"\tvalue:" << value << std::endl;
    //     // 将读到的数据加入到map中
    //     (*_data_pointer)[key].insert(value);
    // }
    // // 清空mysql的结果
    // mysql_free_result(result);


    this->load_route_data();
    this->swap();

}

// 根据modid和cmdid获取主机的信息集合
host_set Route::get_hosts(int modid, int cmdid){
    host_set hosts;

    // 组装map的key
    uint64_t key = ((uint64_t)modid<<32) + cmdid;
    
    // 加锁
    pthread_rwlock_rdlock(&_map_lock);
   
    // 寻找key所对应的value集合
    route_map_it it = _data_pointer->find(key);
    if(it != _data_pointer->end()){
        // 找到对应key的value集合
        hosts = it->second;
    }
    
    // 解锁
    pthread_rwlock_unlock(&_map_lock);

    return hosts;
}

// 得到map中的数据
void Route::test_map(){
    
    // 遍历第一层map
    route_map_it it=_data_pointer->begin();
    for(; it!=_data_pointer->end(); it++){
        
        // 便利第二层set
        host_set_it hit = it->second.begin();
        for(; hit!=it->second.end(); hit++){
            // 将key和value恢复为原来的值
            int modId = (int)((it->first) >> 32);
            int cmdId = (int)(it->first);
            unsigned int serverIp = (int)((*hit)>>32);
            int serverPort = (int)(*hit);
            // std::cout << "key:" << it->first <<"\tvalue:" << *hit << std::endl;
            printf("modId:%d, cmdId:%d, serverIp:%u, serverPort:%d\n", modId, cmdId, serverIp, serverPort);
        
        }
    }
}


// 加载数据版本号
// return 0, 表示 加载成功，version没有改变
//        1, 表示 加载成功，version有改变
//        -1 表示 加载失败
int Route::load_version(){
    
    memset(&_sql, 0, 1000);
    snprintf(_sql, 1000, "SELECT version FROM RouteVersion where id=1;");
    
    int ret = mysql_real_query(&_db_conn, _sql, strlen(_sql));
    if(ret != 0){
        fprintf(stderr, "mysql_real_query error: %s\n", mysql_error(&_db_conn));
        exit(1);
    }

    // 获得一个结果集合
    MYSQL_RES *result = mysql_store_result(&_db_conn);
    if (!result)
    {
        fprintf(stderr, "mysql store result: %s\n", mysql_error(&_db_conn));
        return -1;
    }
    long line_num = mysql_num_rows(result);
    if (line_num == 0)
    {
        fprintf(stderr, "No version in table RouteVersion: %s\n", mysql_error(&_db_conn));
        return -1;
    }
   
    // 得到每列数据
    MYSQL_ROW row = mysql_fetch_row(result);

    // 得到版本号
    long new_version = atol(row[0]);

    // 判断版本信息
    if(new_version == this->_version){
        // 表示数据没有发生变化
        return 0;
    }
    
    // 表示版本发生变化，更新版本数据
    this->_version = new_version;
    printf("now route version is %ld\n", this->_version);

    mysql_free_result(result);
    return 1;
    
}

// 从mysql中加载最新的数据到_temp_pointer中
void Route::load_route_data(){
    // 读数据前先将数据清空
    _temp_pointer->clear();

    // 查询RouteData数据库
    memset(&_sql, 0, 1000);
    snprintf(_sql, 1000, "SELECT * FROM RouteData;");
    int ret = mysql_real_query(&_db_conn, _sql, strlen(_sql));
    if(ret != 0){
        fprintf(stderr, "mysql_real_query error: %s\n", mysql_error(&_db_conn));
        exit(1);
    }

    // 获得一个结果集合
    MYSQL_RES *result = mysql_store_result(&_db_conn);
    // 得到行数
    long line_num = mysql_num_rows(result);

    MYSQL_ROW row;
    // 遍历分析集合中的元素，加入到_data_pointer中
    for(int i=0; i<line_num; i++){
        // 处理一行的数据
        row = mysql_fetch_row(result);

        int modId = atoi(row[1]);
        int cmdId = atoi(row[2]);
        unsigned int serverIp = atoi(row[3]);
        int serverPort = atoi(row[4]);

        // printf("modId:%d, cmdId:%d, serverIp:%u, serverPort:%d\n", modId, cmdId, serverIp, serverPort);
        
        // 组装map的key
        uint64_t key = ((uint64_t)modId<<32) + cmdId;
        // 组装map的value
        uint64_t value = ((uint64_t)serverIp<<32) + serverPort;
        
        // std::cout << "key:" << key <<"\tvalue:" << value << std::endl;
        // 将读到的数据加入到map中
        (*_temp_pointer)[key].insert(value);
    }
    // 清空mysql的结果
    mysql_free_result(result);
}

//将_temp_pointer的数据更新到_data_pointer
void Route::swap(){
    pthread_rwlock_wrlock(&_map_lock);
    route_map* temp = _data_pointer;
    _data_pointer = _temp_pointer;
    _temp_pointer = temp;
    pthread_rwlock_unlock(&_map_lock);
}

//加载RouteChange得到修改的modid/cmdid, 将结果放在vector中
void Route::load_changes(std::vector<uint64_t> &change_list){
    
    memset(&_sql, 0, 1000);
    snprintf(_sql, 1000, "SELECT modid, cmdid FROM RouteChange WHERE version >=%ld;", _version);
    
    int ret = mysql_real_query(&_db_conn, _sql, strlen(_sql));
    if(ret != 0){
        fprintf(stderr, "mysql_real_query error: %s\n", mysql_error(&_db_conn));
        exit(1);
    }

    // 获得一个结果集合
    MYSQL_RES *result = mysql_store_result(&_db_conn);
    if (!result)
    {
        fprintf(stderr, "mysql_store_result %s\n", mysql_error(&_db_conn));
        return ;
    }

    long line_num = mysql_num_rows(result);
    if (line_num == 0)
    {
        fprintf(stderr,  "No version in table ChangeLog: %s\n", mysql_error(&_db_conn));
        return ;
    }

    MYSQL_ROW row;
    // 遍历分析集合中的元素，加入到_data_pointer中
    for(long i=0; i<line_num; i++){
        // 处理一行的数据
        row = mysql_fetch_row(result);

        int modId = atoi(row[0]);
        int cmdId = atoi(row[1]);

        // 组装map的key
        uint64_t key = ((uint64_t)modId<<32) + cmdId;
      
        // 将修改后的mod放入vector中
        change_list.push_back(key);
    }

    // 清空mysql的结果
    mysql_free_result(result);

} 

//删除RouteChange的全部修改记录数据,remove_all为全部删除
//否则默认删除当前版本之前的全部修改
void Route::remove_changes(bool remove_all){
    if (remove_all == false)
    {
        snprintf(_sql, 1000, "DELETE FROM RouteChange WHERE version <= %ld;", _version);
    }
    else
    {
        snprintf(_sql, 1000, "DELETE FROM RouteChange;");
    }

    int ret = mysql_real_query(&_db_conn, _sql, strlen(_sql));
    if (ret != 0)
    {
        fprintf(stderr, "delete RouteChange: %s\n", mysql_error(&_db_conn));
        return ;
    } 

    return;
}



// 用于定期发布已经修改的mod的集合，主要用来测试使用
void* publish_change_mod(void * args){
    // 每隔一秒发一次数据
    while(true){
        sleep(1);
        std::vector<uint64_t> change_mods;
        
        // 假设每次modid=1/cmdid=1和modid=1/cmdid=2被修改
        int modid1 = 1;
        int cmdid1 = 1;
        uint64_t mod1 = ((((uint64_t)modid1)<<32) + cmdid1);

        int modid2 = 1;
        int cmdid2 = 2;
        uint64_t mod2 = ((((uint64_t)modid2)<<32) + cmdid2);

        change_mods.push_back(mod1);
        change_mods.push_back(mod2);
        SubscribeList::instance()->publish(change_mods);
    }
}


// 周期行后端检查数据库中的route信息的更改信息
// 两个业务: 1.定期检查版本信息，2.固定时间更新数据
void *check_route_change(void *args){
    // 10s自动加载一次数据
    int wait_time =10;
    // 记录当前事件
    long last_load_time = time(NULL);

    while(true){
        //每1s检查一次版本信息
        sleep(1);

        // 业务1，判断版本是否被修改
        int ret = Route::instance()->load_version();
        if(ret == 1){
            // 说明版本信息更改，modid和cmdid更改
            printf("Route data change!\n");
            // 1.将最新的RouteData数据加载到_temp_pointer中
            Route::instance()->load_route_data();

            // 2.将_temp_pointer中的数据更新到_data_pointer中
            Route::instance()->swap();
            // 更新时间
            last_load_time = time(NULL);

            // 3.获取当前已经被修改的modid和cmdid集合
            std::vector<uint64_t> changemod;
            Route::instance()->load_changes(changemod);

            // 4.向订阅修改的mod客户端agent推送消息
            SubscribeList::instance()->publish(changemod);

            // 5.删除版本记录
            // Route::instance()->remove_changes(false);

        }
        else if(ret == 0){
            // 说明版本信息未更改，固定时间强制更新_data_pointer数据
            if((time(NULL)-last_load_time)>=wait_time){            
                // 将最新的RouteData数据加载到_temp_pointer中
                Route::instance()->load_route_data();
                // 将_temp_pointer中的数据更新到_data_pointer中
                Route::instance()->swap();

                 // 更新时间
                 last_load_time = time(NULL);
            }
        }
        else{
            fprintf(stderr, "Route::instance()->load_version() error\n");
        }

    }
    
}